"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractZipArchive = void 0;
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importStar(require("path"));
const os_1 = require("os");
const extract_zip_1 = __importDefault(require("extract-zip"));
const logger_1 = require("./logger");
const lang_1 = require("../utils/lang");
const FileSystemError_1 = require("../models/FileSystemError");
const fs_1 = require("./fs");
const i18nKey = 'lib.archive';
async function extractZip(name, zip, hideLogs = false) {
    const result = { extractDir: '', tmpDir: '' };
    const TMP_FOLDER_PREFIX = `hubspot-temp-${name}-`;
    if (!hideLogs) {
        logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.extractZip.init`));
    }
    // Write zip to disk
    let tmpZipPath = '';
    try {
        result.tmpDir = await fs_extra_1.default.mkdtemp((0, path_1.join)((0, os_1.tmpdir)(), TMP_FOLDER_PREFIX));
        tmpZipPath = (0, path_1.join)(result.tmpDir, 'hubspot-temp.zip');
        await fs_extra_1.default.ensureFile(tmpZipPath);
        await fs_extra_1.default.writeFile(tmpZipPath, zip, {
            mode: 0o777,
        });
    }
    catch (err) {
        if (tmpZipPath || result.tmpDir) {
            throw new FileSystemError_1.FileSystemError({ cause: err }, {
                filepath: tmpZipPath || result.tmpDir,
                operation: 'write',
            });
        }
        else {
            throw new Error((0, lang_1.i18n)(`${i18nKey}.extractZip.errors.write`), {
                cause: err,
            });
        }
    }
    // Extract zip
    try {
        const tmpExtractPath = (0, path_1.join)(result.tmpDir, 'extracted');
        await (0, extract_zip_1.default)(tmpZipPath, { dir: tmpExtractPath });
        result.extractDir = tmpExtractPath;
    }
    catch (err) {
        throw new Error((0, lang_1.i18n)(`${i18nKey}.extractZip.errors.extract`), {
            cause: err,
        });
    }
    logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.extractZip.success`));
    return result;
}
async function copySourceToDest(src, dest, { sourceDir, includesRootDir = true, hideLogs = false, handleCollision, } = {}) {
    try {
        if (!hideLogs) {
            logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.copySourceToDest.init`));
        }
        const srcDirPath = [src];
        if (includesRootDir) {
            const files = await fs_extra_1.default.readdir(src);
            const rootDir = files[0];
            if (!rootDir) {
                logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.copySourceToDest.sourceEmpty`));
                // Create the dest path if it doesn't already exist
                await fs_extra_1.default.ensureDir(dest);
                // No root found so nothing to copy
                return true;
            }
            srcDirPath.push(rootDir);
        }
        const sourceDirs = [];
        if (sourceDir) {
            sourceDirs.push(...(Array.isArray(sourceDir) ? new Set(sourceDir) : [sourceDir]));
        }
        if (sourceDirs.length === 0) {
            const projectSrcDir = (0, path_1.join)(...srcDirPath);
            await fs_extra_1.default.copy(projectSrcDir, dest);
        }
        else {
            for (let i = 0; i < sourceDirs.length; i++) {
                const projectSrcDir = (0, path_1.join)(...srcDirPath, sourceDirs[i]);
                let collisions = [];
                let filesWithoutCollisions = [];
                if (fs_extra_1.default.existsSync(dest) &&
                    handleCollision &&
                    typeof handleCollision === 'function') {
                    const existingFiles = (await (0, fs_1.walk)(dest, ['node_modules'])).map(file => path_1.default.normalize(path_1.default.relative(dest, file)));
                    const newFiles = (await (0, fs_1.walk)(projectSrcDir, ['node_modules'])).map(file => path_1.default.relative(projectSrcDir, file));
                    // Find files that exist in the same positions in both directories
                    collisions = existingFiles.filter(currentFile => newFiles.includes(currentFile));
                    filesWithoutCollisions = newFiles.filter(currentFile => !collisions.includes(currentFile));
                }
                if (collisions.length &&
                    handleCollision &&
                    typeof handleCollision === 'function') {
                    await handleCollision({
                        dest,
                        src: projectSrcDir,
                        collisions,
                    });
                    await Promise.all(filesWithoutCollisions.map(currentFile => fs_extra_1.default.copy(path_1.default.join(projectSrcDir, currentFile), path_1.default.join(dest, currentFile))));
                }
                else {
                    await fs_extra_1.default.copy(projectSrcDir, dest);
                }
            }
        }
        logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.copySourceToDest.success`));
        return true;
    }
    catch (err) {
        logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.copySourceToDest.error`, { dest }));
        throw new FileSystemError_1.FileSystemError({ cause: err }, {
            filepath: dest,
            operation: 'write',
        });
    }
}
async function cleanupTempDir(tmpDir) {
    if (!tmpDir)
        return;
    try {
        await fs_extra_1.default.remove(tmpDir);
    }
    catch (e) {
        logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.cleanupTempDir.error`, { tmpDir }));
    }
}
async function extractZipArchive(zip, name, dest, { sourceDir, includesRootDir, hideLogs, handleCollision, } = {}) {
    let success = false;
    if (zip) {
        const { extractDir, tmpDir } = await extractZip(name, zip, hideLogs);
        if (extractDir !== null) {
            success = await copySourceToDest(extractDir, dest, {
                sourceDir,
                includesRootDir,
                hideLogs,
                handleCollision,
            });
        }
        await cleanupTempDir(tmpDir);
    }
    return success;
}
exports.extractZipArchive = extractZipArchive;
